{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "59f5be7e",
   "metadata": {},
   "source": [
    "# GaussianNB 高斯朴素贝叶斯\n",
    "\n",
    "特征的可能性被假设为高斯\n",
    "\n",
    "概率密度函数：\n",
    "$$P(x_i | y_k)=\\frac{1}{\\sqrt{2\\pi\\sigma^2_{yk}}}exp(-\\frac{(x_i-\\mu_{yk})^2}{2\\sigma^2_{yk}})$$\n",
    "\n",
    "数学期望(mean)：$\\mu$\n",
    "\n",
    "方差：$\\sigma^2=\\frac{\\sum(X-\\mu)^2}{N}$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "f8346aab",
   "metadata": {},
   "source": [
    "import numpy as np\n",
    "import pandas as pd\n",
    "import matplotlib.pyplot as plt\n",
    "%matplotlib inline\n",
    "\n",
    "from sklearn.datasets import load_iris\n",
    "from sklearn.model_selection import train_test_split\n",
    "\n",
    "from collections import Counter\n",
    "import math\n",
    "\n",
    "# data\n",
    "def create_data():\n",
    "    iris = load_iris()\n",
    "    df = pd.DataFrame(iris.data, columns=iris.feature_names)\n",
    "    df['label'] = iris.target\n",
    "    df.columns = [\n",
    "        'sepal length', 'sepal width', 'petal length', 'petal width', 'label'\n",
    "    ]\n",
    "    data = np.array(df.iloc[:100, :])\n",
    "    # print(data)\n",
    "    return data[:, :-1], data[:, -1]\n",
    "\n",
    "X, y = create_data()\n",
    "X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3)\n",
    "\n",
    "\n",
    "class NaiveBayes:\n",
    "    def __init__(self):\n",
    "        self.model = None\n",
    "        \n",
    "    # 数学期望\n",
    "    @staticmethod\n",
    "    def mean(X):\n",
    "        return sum(X) / float(len(X))\n",
    "    \n",
    "    # 标准差\n",
    "    def stdev(self, X):\n",
    "        avg = self.mean(X)\n",
    "        return math.sqrt(sum([pow(x - avg, 2) for x in X]) / float(len(X)))\n",
    "    \n",
    "    # 概率密度函数\n",
    "    def gaussian_probability(self, x, mean, stdev):\n",
    "        exponent = math.exp(-(math.pow(x - mean, 2) / (2 * math.pow(stdev, 2))))\n",
    "        return (1 / (math.sqrt(2 * math.pi) * stdev)) * exponent\n",
    "        \n",
    "    # 计算每个特征的均值和标准差\n",
    "    # 处理X_train\n",
    "    def summarize(self, train_data):\n",
    "        # * 符号用于解包操作。当应用在一个可迭代对象（如列表、元组、数组等）前面时，它会将这个可迭代对象解包成独立的元素。\n",
    "        # 对于 NumPy 数组 train_data，*train_data 的作用是将数组 train_data 解包成独立的元素。\n",
    "        # 具体来说，如果 train_data 是一个二维数组，解包操作将会将其按行解包成多个一维数组。\n",
    "        return [(self.mean(i), self.stdev(i)) for i in zip(*train_data)]\n",
    "    \n",
    "    # 分别求出数学期望和标准差\n",
    "    def fit(self, X, y):\n",
    "        labels = list(set(y))\n",
    "        # 根据类标签索引训练数据中属于该类标签的样本\n",
    "        data = {label: [] for label in labels}\n",
    "        for f, label in zip(X, y):\n",
    "            data[label].append(f)\n",
    "        self.model = {\n",
    "            label: self.summarize(value) for label, value in data.items()\n",
    "        }\n",
    "        return 'gaussianNB train done!'\n",
    "    \n",
    "    # 计算概率\n",
    "    def calculate_probabilities(self, input_data):\n",
    "        # summaries:{0.0: [(5.0, 0.37),(3.42, 0.40)], 1.0: [(5.8, 0.449),(2.7, 0.27)]}\n",
    "        # input_data:[1.1, 2.2]\n",
    "        probabilities = {}\n",
    "        for label, value in self.model.items():\n",
    "            probabilities[label] = 1\n",
    "            # 遍历每个特征值的均值和标准差\n",
    "            for i in range(len(value)):\n",
    "                mean, stdev = value[i]\n",
    "                probabilities[label] *= self.gaussian_probability(input_data[i], mean, stdev)\n",
    "                \n",
    "        return probabilities\n",
    "    \n",
    "    # 类别\n",
    "    def predict(self, X_test):\n",
    "        # {0.0: 2.9680340789325763e-27, 1.0: 3.5749783019849535e-26}\n",
    "        # 按照{label: probability}中概率升序排序，排序后取概率值最大项的类别\n",
    "        label = sorted(self.calculate_probabilities(X_test).items(), key=lambda x: x[-1])[-1][0]\n",
    "        return label\n",
    "    \n",
    "    def score(self, X_test, y_test):\n",
    "        right = 0\n",
    "        for X, y in zip(X_test, y_test):\n",
    "            label = self.predict(X)\n",
    "            if label == y:\n",
    "                right += 1\n",
    "                \n",
    "        return right / float(len(X_test))\n",
    "    \n",
    "model = NaiveBayes()\n",
    "model.fit(X_train, y_train)"
   ],
   "outputs": []
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "f75dc776",
   "metadata": {},
   "source": [
    "print(model.predict([4.4, 3.2, 1.3, 0.2]))"
   ],
   "outputs": []
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "6049e666",
   "metadata": {},
   "source": [
    "model.score(X_test, y_test)"
   ],
   "outputs": []
  },
  {
   "cell_type": "markdown",
   "id": "1778bb64",
   "metadata": {},
   "source": [
    "# scikit-learn中的Bayes\n",
    "## 特征值符合高斯分布时"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "c23f95ee",
   "metadata": {},
   "source": [
    "from sklearn.naive_bayes import GaussianNB\n",
    "\n",
    "clf = GaussianNB()\n",
    "clf.fit(X_train, y_train)\n",
    "print(f'The score used GaussianNB to predict is {clf.score(X_test, y_test) * 100}%')"
   ],
   "outputs": []
  },
  {
   "cell_type": "markdown",
   "id": "63b84880",
   "metadata": {},
   "source": [
    "## 特征值符合伯努利分布时"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "1b91c545",
   "metadata": {},
   "source": [
    "from sklearn.naive_bayes import BernoulliNB\n",
    "\n",
    "clf = BernoulliNB()\n",
    "clf.fit(X_train, y_train)\n",
    "print(f'The score used BernoulliNB to predict is {clf.score(X_test, y_test) * 100}%')"
   ],
   "outputs": []
  },
  {
   "cell_type": "markdown",
   "id": "3367db4a",
   "metadata": {},
   "source": [
    "## 特征值符合多项式分布时"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "103e4265",
   "metadata": {},
   "source": [
    "from sklearn.naive_bayes import MultinomialNB\n",
    "\n",
    "clf = MultinomialNB()\n",
    "clf.fit(X_train, y_train)\n",
    "print(f'The score used MultinomialNB to predict is {clf.score(X_test, y_test) * 100}%')"
   ],
   "outputs": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.5"
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": true,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {},
   "toc_section_display": true,
   "toc_window_display": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
